/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "athd.h"

/* begin_private */
static athd_conf * _current_conf = NULL;

static athd_conf * _athd_conf_alloc(apr_pool_t *pool);
static athd_conf_os * _athd_conf_os_alloc(apr_pool_t *pool);
/* end_private */


athd_conf * 
athd_conf_create(apr_pool_t *pool, const char *str_path, 
		 const athd_conf_prop *props)
{
    athd_conf *conf;
    int rv;
    const char *str_err = NULL;
    const athd_conf_prop *prop;
    apr_file_t *conf_fh;
    athd_directive *dtree;
    athd_directive *dcur;
    athd_tree_state tstate = ATHD_TREE_VIS;
    char *str_debug = "";

    ASSERT(str_path && props);
    conf = _athd_conf_alloc(pool);

    /* set prefile defaults */
    conf->port = ATHD_DEFAULT_PORT;
    conf->pidfile = ATHD_DEFAULT_PIDFILE;
    conf->logfile = ATHD_DEFAULT_LOGFILE;
    conf->log_lock_path = ATHD_DEFAULT_LOGLOCK;
    conf->os->path_shell = ATHD_DEFAULT_SHELL;

    str_err = athd_directive_gen_tree(conf->pool, &dtree, str_path);
    if (str_err) 
      {
	  athd_log(ATHD_CRIT, "Critical config error: %s", str_err);
	  athd_proc_exit(1);
      }    
    /* start at top, 1st iter */
    dcur = dtree;
    athd_directive_traverse(&dcur, &tstate);

    while (dcur)
      {
	  prop = athd_conf_prop_get(props, dcur->cmd, dcur->parent->cmd);
	  if (prop)
	    {
		char *arg = NULL;
		char *pararg = NULL;
		ath_node *listctx1, *listctx2;
		ath_list_rewind_r(dcur->args, &listctx1);
		/* really, parent only has one arg (its name) (i hope) */
		if (! ath_list_isempty(dcur->parent->args))
		  {
		      ath_list_rewind_r(dcur->parent->args, &listctx2);
		      pararg = (char *) 
			ath_list_getnext_r(dcur->parent->args, &listctx2);
		  }
		while (ath_list_hasnext_r(dcur->args, &listctx1))
		  {
		      arg = (char *) ath_list_getnext_r(dcur->args, &listctx1);
		      str_err = athd_conf_prop_set(conf, prop, arg, pararg);
		      if (str_err) 
			  athd_log(ATHD_WARN, 
				   "Config: %s arg: %s pararg: %s", 
				   str_err, arg, pararg);
		  }
	    }
	  else
	      ATHD_PRINTERR(ATHD_ERR_NOPROP, dcur->cmd);
	  /* recurse till null back at top */
	  athd_directive_traverse(&dcur, &tstate);
      }
    /* set post file parse defaults, check for requireds */

    if (conf->server_name == NULL)
	athd_conf_prop_name(conf, NULL, NULL);

    if (conf->uid == -1)
      {
	  str_err = athd_conf_prop_uid(conf, ATHD_DEFAULT_USER, NULL);
	  if (str_err)
	      athd_log(ATHD_WARN, "Config uid: %s", str_err);
      }
    
    if (((conf->os->path_sendmail == NULL))
	|| (!(*conf->os->path_sendmail)))
	conf->os->path_sendmail = ATHD_DEFAULT_SENDMAIL;

    return conf;
}

void
athd_conf_destroy(athd_conf **conf)
{
    apr_pool_t *pool;
    pool = (*conf)->pool;
    apr_pool_destroy(pool);
    (*conf) = NULL;
}

athd_conf * 
athd_conf_overrides(athd_conf *conf, 
		    const athd_conf_prop *props, 
		    ath_list *propovers)
{
    int i;
    char *sep = " \t";
    char *line, *str_cmd, *str_arg, *inter;
    const char *str_err = NULL;
    const athd_conf_prop *prop;
    ASSERT(conf);
    ASSERT(propovers);

    for (i = 0; i < ath_list_size(propovers); i++)
      {
	  line = ath_list_getnext(propovers);
	  str_cmd = apr_strtok(line, sep, &inter);
	  prop = athd_conf_prop_get(props, str_cmd, NULL);
	  if (prop)
	    {
		str_arg = apr_strtok(NULL, sep, &inter);
		str_err = athd_conf_prop_set(conf, prop, str_arg, NULL);
		if (str_err)
		    athd_log(ATHD_CRIT, ATHD_ERR_DPROP, 
			     prop->name, str_arg, str_err);
	    }
	  else /* prop doesn't exist */
	      athd_log(ATHD_WARN, ATHD_ERR_NOPROP, str_cmd);
      }
    if (str_err) athd_proc_exit(1);
    return conf;
}

void
athd_conf_current_set(athd_conf *conf)
{
    _current_conf = conf;
}

athd_conf *
athd_conf_current_get()
{
    return _current_conf;
}

char * 
athd_conf_to_str(apr_pool_t *pool, athd_conf *conf)
{
    char *str_out = "\n";
    char *user, *group;
    int i,j;
    char *str;
    athd_job *job;
    athd_engine *engine;
    ASSERT(conf);
  
    apr_uid_name_get(&user, conf->uid, pool);
    apr_gid_name_get(&group, conf->gid, pool);

    str_out = apr_pstrcat(pool, 
			  "server_name: ", conf->server_name,
			  "\nuser: ", user, 
			  "\ngroup: ", group, "\n", NULL);
    str_out = apr_pstrcat(pool, str_out, 
			  (apr_psprintf(pool, 
					"port: %d\n", conf->port)),
			  NULL);
    str_out = apr_pstrcat(pool, str_out, "pidfile: ",
			  conf->pidfile, "\n", NULL);
    str_out = apr_pstrcat(pool, str_out, "logfile: ",
			  conf->logfile, "\n", NULL);
    str_out = apr_pstrcat(pool, str_out, "shell: ",
			  conf->os->path_shell, "\n", NULL);
    str_out = apr_pstrcat(pool, str_out, "sendmail: ",
			  conf->os->path_sendmail, "\n", NULL);
    ath_list_rewind(conf->engines);
    for (i = 0; i < ath_list_size(conf->engines); i++)
      {
	  engine = (athd_engine *) ath_list_getnext(conf->engines);
	  str_out = apr_pstrcat(pool, str_out, 
				(athd_engine_to_str(pool, engine)));
      }
    for (i = 0; i < ath_list_size(conf->jobs); i++)
      {
	  int j;
	  job = (athd_job *) ath_list_getnext(conf->jobs);
	  str_out = apr_pstrcat(pool, str_out,
				(athd_job_to_str(pool, job)));
      }

    return str_out;
}

/* prop -> function mapping is a 2-tuple on prop name and parent name */
/* eg. these return different functions: */
/* ATHD_CONF_PROP("Engine", athd_conf_prop_eng, NULL) */
/* ATHD_CONF_PROP("Engine", athd_conf_prop_eng, "Job") */
/* ATHD_CONF_PROP("Job", athd_conf_prop_eng, NULL) */
/* ATHD_CONF_PROP("Job", athd_conf_prop_job, "Engine") */
const athd_conf_prop * 
athd_conf_prop_get(const athd_conf_prop *props, char *cmd, char *parcmd)
{
    int i = 0;
    int len;
    const athd_conf_prop *prop = &props[i];
    ASSERT(cmd);
    while (prop->name != NULL)
      {
	  if (ath_streq(cmd, prop->name))
	    {
		if ((prop->parent) && (parcmd)
		    && (ath_streq(prop->parent, parcmd)))
		    return prop;
		else if ((prop->parent == NULL) && (parcmd == NULL))
		    return prop;
	    }
	  i++;
	  prop = &props[i];
      }
    return NULL;
}

const char * 
athd_conf_prop_set(athd_conf *conf, 
		   const athd_conf_prop *prop, 
		   char *arg, char *pararg)
{
    const char *msg = NULL;
    ASSERT (conf && prop);
    msg = (prop->func)(conf, arg, pararg);
    return msg;
}

athd_ip_farm *
athd_conf_ip_farm_add(athd_conf *conf, const char *name, 
		      int farmport, int physport)
{
    ath_node *ctx;
    athd_ip_farm *farm = NULL;
    ASSERT(name);
    ath_list_rewind_r(conf->ip_farms, &ctx);
    while ((ath_list_hasnext_r(conf->ip_farms, &ctx))
	   && (!farm))
      {
	  farm = (athd_ip_farm *)
	    ath_list_getnext_r(conf->ip_farms, &ctx);
	  if (strcasecmp(farm->name, name))
	      farm = NULL;
      }
    if (!farm)
      {
	  farm = apr_pcalloc(conf->pool, sizeof(athd_ip_farm));
	  farm->name = apr_pstrdup(conf->pool, name);
	  farm->farm_port = -1;
	  farm->phys_port = -1;
      }
    if (farmport > 0)
	farm->farm_port = farmport;
    if (physport > 0)
	farm->phys_port = physport;
    return farm;
}


static athd_conf * 
_athd_conf_alloc(apr_pool_t *pool)
{
    apr_pool_t *cpool;
    athd_conf *conf;
    apr_pool_create(&cpool, pool);
    conf = (athd_conf *) apr_pcalloc(cpool, sizeof(athd_conf));
    conf->pool = cpool;
    conf->debug = -1;
    conf->server_name = NULL;
    conf->use_shm = NULL;
    conf->use_apconf = NULL;
    conf->engines = ath_list_ap_alloc(conf->pool);
    athd_engine_cache_init(conf->pool);
    conf->uid = -1;
    conf->gid = -1;
    conf->pidfile = NULL;
    conf->logfile = NULL;
    conf->syslog_fac = 0;
    conf->log_lock_path = NULL;
    conf->os = _athd_conf_os_alloc(conf->pool);
    conf->job_proto = athd_job_create(conf->pool, ATHD_JOB_PROTO_NAME);
    conf->jobs = ath_list_ap_alloc(conf->pool);
    athd_job_cache_init(conf->pool);
    conf->ip_lb = 0;
    conf->ip_divertport = -1;
    conf->ip_deny = 1;
    conf->ip_sockets = 1;
    conf->ip_farms = ath_list_ap_alloc(conf->pool);
    conf->stats_pulse = ATHD_DEFAULT_STATS_PULSE;
    return conf;
}

static athd_conf_os * 
_athd_conf_os_alloc(apr_pool_t *pool)
{
    athd_conf_os *os_conf = 
      (athd_conf_os *) apr_pcalloc(pool, sizeof(athd_conf_os));
    ASSERT (os_conf);
    os_conf->path_shell = NULL;
    os_conf->path_sendmail = NULL;
    return os_conf;
}
